package com.yupi.yuoj.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.yuoj.common.ErrorCode;
import com.yupi.yuoj.constant.CommonConstant;
import com.yupi.yuoj.exception.BusinessException;
import com.yupi.yuoj.exception.ThrowUtils;
import com.yupi.yuoj.mapper.ContestMapper;
import com.yupi.yuoj.mapper.ContestQuestionMapper;
import com.yupi.yuoj.model.dto.contest.ContestAddRequest;
import com.yupi.yuoj.model.dto.contest.ContestEditRequest;
import com.yupi.yuoj.model.dto.contest.ContestQueryRequest;
import com.yupi.yuoj.model.dto.contest.ContestRegistrationRequest;
import com.yupi.yuoj.model.entity.Contest;
import com.yupi.yuoj.model.entity.ContestQuestion;
import com.yupi.yuoj.model.entity.ContestRegistration;
import com.yupi.yuoj.model.entity.Question;
import com.yupi.yuoj.model.entity.User;
import com.yupi.yuoj.model.vo.ContestVO;
import com.yupi.yuoj.model.vo.QuestionVO;
import com.yupi.yuoj.model.vo.UserVO;
import com.yupi.yuoj.service.ContestQuestionService;
import com.yupi.yuoj.service.ContestRegistrationService;
import com.yupi.yuoj.service.ContestService;
import com.yupi.yuoj.service.QuestionService;
import com.yupi.yuoj.service.UserService;
import com.yupi.yuoj.utils.SqlUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 竞赛服务实现
 */
@Service
public class ContestServiceImpl extends ServiceImpl<ContestMapper, Contest> implements ContestService {

    @Resource
    private UserService userService;

    @Resource
    private QuestionService questionService;

    @Resource
    private ContestQuestionService contestQuestionService;

    @Resource
    private ContestRegistrationService contestRegistrationService;

    /**
     * 校验竞赛
     *
     * @param contest
     * @param add
     */
    @Override
    public void validContest(Contest contest, boolean add) {
        if (contest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String title = contest.getTitle();
        Date startTime = contest.getStartTime();
        Integer duration = contest.getDuration();

        // 创建时，参数不能为空
        if (add) {
            ThrowUtils.throwIf(StringUtils.isAnyBlank(title), ErrorCode.PARAMS_ERROR, "竞赛标题不能为空");
            ThrowUtils.throwIf(startTime == null, ErrorCode.PARAMS_ERROR, "开始时间不能为空");
            ThrowUtils.throwIf(duration == null || duration <= 0, ErrorCode.PARAMS_ERROR, "竞赛时长不能为空或小于等于0");
        }
        // 有参数则校验
        if (StringUtils.isNotBlank(title) && title.length() > 80) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "竞赛标题过长");
        }
    }

    /**
     * 获取查询条件
     *
     * @param contestQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Contest> getQueryWrapper(ContestQueryRequest contestQueryRequest) {
        QueryWrapper<Contest> queryWrapper = new QueryWrapper<>();
        if (contestQueryRequest == null) {
            return queryWrapper;
        }
        Long id = contestQueryRequest.getId();
        String title = contestQueryRequest.getTitle();
        Integer contestType = contestQueryRequest.getContestType();
        Integer status = contestQueryRequest.getStatus();
        Long userId = contestQueryRequest.getUserId();
        String searchText = contestQueryRequest.getSearchText();
        String sortField = contestQueryRequest.getSortField();
        String sortOrder = contestQueryRequest.getSortOrder();

        // 拼接查询条件
        queryWrapper.eq(id != null, "id", id);
        queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
        queryWrapper.eq(contestType != null, "contestType", contestType);
        queryWrapper.eq(status != null, "status", status);
        queryWrapper.eq(userId != null, "userId", userId);
        queryWrapper.like(StringUtils.isNotBlank(searchText), "title", searchText);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取竞赛VO
     *
     * @param contest
     * @param request
     * @return
     */
    @Override
    public ContestVO getContestVO(Contest contest, HttpServletRequest request) {
        if (contest == null) {
            return null;
        }
        ContestVO contestVO = ContestVO.objToVo(contest);

        // 1. 关联创建人信息
        Long userId = contest.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        contestVO.setUserVO(userVO);

        // 2. 获取参赛人数
        long contestId = contest.getId();
        int registrationCount = contestRegistrationService.getRegistrationCount(contestId);
        contestVO.setRegistrationCount(registrationCount);

        // 3. 获取关联的题目数量
        QueryWrapper<ContestQuestion> questionQueryWrapper = new QueryWrapper<>();
        questionQueryWrapper.eq("contestId", contestId);
        long questionCount = contestQuestionService.count(questionQueryWrapper);
        contestVO.setQuestionCount((int) questionCount);

        // 4. 获取当前用户是否已注册
        User loginUser = userService.getLoginUser(request);
        if (loginUser != null) {
            boolean hasRegistered = contestRegistrationService.hasUserRegistered(contestId, loginUser.getId());
            contestVO.setHasRegistered(hasRegistered);
        }

        return contestVO;
    }

    /**
     * 分页获取竞赛VO
     *
     * @param contestPage
     * @param request
     * @return
     */
    @Override
    public Page<ContestVO> getContestVOPage(Page<Contest> contestPage, HttpServletRequest request) {
        List<Contest> contestList = contestPage.getRecords();
        Page<ContestVO> contestVOPage = new Page<>(contestPage.getCurrent(), contestPage.getSize(), contestPage.getTotal());
        if (CollectionUtils.isEmpty(contestList)) {
            return contestVOPage;
        }

        // 1. 获取关联的用户信息
        Set<Long> userIdSet = contestList.stream().map(Contest::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));

        // 2. 获取所有竞赛ID
        Set<Long> contestIdSet = contestList.stream().map(Contest::getId).collect(Collectors.toSet());

        // 3. 获取关联的参赛人数信息
        Map<Long, Integer> contestIdRegistrationCountMap = new java.util.HashMap<>();
        for (Long contestId : contestIdSet) {
            int count = contestRegistrationService.getRegistrationCount(contestId);
            contestIdRegistrationCountMap.put(contestId, count);
        }

        // 4. 获取关联的题目数量信息
        Map<Long, Long> contestIdQuestionCountMap = new java.util.HashMap<>();
        for (Long contestId : contestIdSet) {
            QueryWrapper<ContestQuestion> questionQueryWrapper = new QueryWrapper<>();
            questionQueryWrapper.eq("contestId", contestId);
            long count = contestQuestionService.count(questionQueryWrapper);
            contestIdQuestionCountMap.put(contestId, count);
        }

        // 5. 获取当前用户是否已注册信息
        User loginUser = userService.getLoginUser(request);
        Map<Long, Boolean> contestIdUserRegisteredMap = new java.util.HashMap<>();
        if (loginUser != null) {
            Long loginUserId = loginUser.getId();
            for (Long contestId : contestIdSet) {
                boolean hasRegistered = contestRegistrationService.hasUserRegistered(contestId, loginUserId);
                contestIdUserRegisteredMap.put(contestId, hasRegistered);
            }
        }

        // 填充信息
        List<ContestVO> contestVOList = contestList.stream().map(contest -> {
            ContestVO contestVO = ContestVO.objToVo(contest);
            Long contestUserId = contest.getUserId();
            // 创建人信息
            if (userIdUserListMap.containsKey(contestUserId)) {
                User user = userIdUserListMap.get(contestUserId).get(0);
                UserVO userVO = userService.getUserVO(user);
                contestVO.setUserVO(userVO);
            }
            // 参赛人数
            Long contestId = contest.getId();
            contestVO.setRegistrationCount(contestIdRegistrationCountMap.getOrDefault(contestId, 0));
            // 题目数量
            contestVO.setQuestionCount(contestIdQuestionCountMap.getOrDefault(contestId, 0L).intValue());
            // 当前用户是否已注册
            contestVO.setHasRegistered(contestIdUserRegisteredMap.getOrDefault(contestId, false));
            return contestVO;
        }).collect(Collectors.toList());

        contestVOPage.setRecords(contestVOList);
        return contestVOPage;
    }

    /**
     * 添加竞赛
     *
     * @param contestAddRequest
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public long addContest(ContestAddRequest contestAddRequest, HttpServletRequest request) {
        if (contestAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        
        // 1. 处理竞赛基本信息
        Contest contest = new Contest();
        BeanUtils.copyProperties(contestAddRequest, contest);
        // 默认状态为未开始
        contest.setStatus(0);
        // 设置创建人
        contest.setUserId(loginUser.getId());
        // 校验参数
        validContest(contest, true);
        // 保存竞赛信息
        boolean saveResult = this.save(contest);
        if (!saveResult) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "保存竞赛失败");
        }
        Long contestId = contest.getId();
        
        // 2. 处理竞赛题目关系
        List<Long> questionIds = contestAddRequest.getQuestionIds();
        if (CollectionUtils.isNotEmpty(questionIds)) {
            // 验证题目是否存在
            List<Question> questions = questionService.listByIds(questionIds);
            if (questions.size() != questionIds.size()) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "存在无效的题目ID");
            }
            
            // 保存竞赛题目关系
            List<ContestQuestion> contestQuestionList = new ArrayList<>();
            for (int i = 0; i < questionIds.size(); i++) {
                ContestQuestion contestQuestion = new ContestQuestion();
                contestQuestion.setContestId(contestId);
                contestQuestion.setQuestionId(questionIds.get(i));
                contestQuestion.setQuestionOrder(i + 1); // 从1开始的序号
                contestQuestionList.add(contestQuestion);
            }
            boolean batchSaveResult = contestQuestionService.saveBatch(contestQuestionList);
            if (!batchSaveResult) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "保存竞赛题目关系失败");
            }
        }
        
        return contestId;
    }

    /**
     * 更新竞赛
     *
     * @param contestEditRequest
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateContest(ContestEditRequest contestEditRequest, HttpServletRequest request) {
        if (contestEditRequest == null || contestEditRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        
        // 1. 查询竞赛是否存在
        Long contestId = contestEditRequest.getId();
        Contest oldContest = this.getById(contestId);
        if (oldContest == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        
        // 2. 校验权限 - 只有管理员或创建者可以编辑
        if (!userService.isAdmin(loginUser) && !oldContest.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        
        // 3. 如果竞赛已经开始，不允许修改开始时间和时长
        if (oldContest.getStatus() > 0) {
            if (contestEditRequest.getStartTime() != null && !contestEditRequest.getStartTime().equals(oldContest.getStartTime())) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "竞赛已开始，不能修改开始时间");
            }
            if (contestEditRequest.getDuration() != null && !contestEditRequest.getDuration().equals(oldContest.getDuration())) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "竞赛已开始，不能修改时长");
            }
        }
        
        // 4. 更新竞赛基本信息
        Contest contest = new Contest();
        BeanUtils.copyProperties(contestEditRequest, contest);
        validContest(contest, false);
        boolean updateResult = this.updateById(contest);
        if (!updateResult) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新竞赛失败");
        }
        
        // 5. 更新竞赛题目关系
        List<Long> questionIds = contestEditRequest.getQuestionIds();
        if (CollectionUtils.isNotEmpty(questionIds)) {
            // 验证题目是否存在
            List<Question> questions = questionService.listByIds(questionIds);
            if (questions.size() != questionIds.size()) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "存在无效的题目ID");
            }
            
            // 先删除原有关系
            QueryWrapper<ContestQuestion> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("contestId", contestId);
            contestQuestionService.remove(queryWrapper);
            
            // 保存新的关系
            List<ContestQuestion> contestQuestionList = new ArrayList<>();
            for (int i = 0; i < questionIds.size(); i++) {
                ContestQuestion contestQuestion = new ContestQuestion();
                contestQuestion.setContestId(contestId);
                contestQuestion.setQuestionId(questionIds.get(i));
                contestQuestion.setQuestionOrder(i + 1); // 从1开始的序号
                contestQuestionList.add(contestQuestion);
            }
            boolean batchSaveResult = contestQuestionService.saveBatch(contestQuestionList);
            if (!batchSaveResult) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新竞赛题目关系失败");
            }
        }
        
        return true;
    }

    /**
     * 用户注册竞赛
     *
     * @param contestRegistrationRequest
     * @param request
     * @return
     */
    @Override
    public ContestRegistration registerContest(ContestRegistrationRequest contestRegistrationRequest, HttpServletRequest request) {
        if (contestRegistrationRequest == null || contestRegistrationRequest.getContestId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        
        Long contestId = contestRegistrationRequest.getContestId();
        Long userId = loginUser.getId();
        
        // 1. 检查竞赛是否存在
        Contest contest = this.getById(contestId);
        if (contest == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        }
        
        // 2. 检查竞赛是否已结束
        if (contest.getStatus() == 2) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "竞赛已结束，不能报名");
        }
        
        // 3. 检查用户是否已注册
        if (contestRegistrationService.hasUserRegistered(contestId, userId)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "您已经注册过该竞赛");
        }
        
        // 4. 检查竞赛类型和密码
        Integer contestType = contest.getContestType();
        if (contestType == 1) { // 密码保护类型
            String password = contestRegistrationRequest.getPassword();
            if (StringUtils.isBlank(password) || !password.equals(contest.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "竞赛密码错误");
            }
        } else if (contestType == 2) { // 私有类型
            // 检查是否是管理员或创建者
            if (!userService.isAdmin(loginUser) && !contest.getUserId().equals(userId)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "该竞赛为私有竞赛，您无权参加");
            }
        }
        
        // 5. 创建注册记录
        ContestRegistration contestRegistration = new ContestRegistration();
        contestRegistration.setContestId(contestId);
        contestRegistration.setUserId(userId);
        contestRegistration.setStatus(0); // 已注册状态
        contestRegistration.setTotalScore(0);
        contestRegistration.setTotalPenalty(0);
        
        boolean saveResult = contestRegistrationService.save(contestRegistration);
        if (!saveResult) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "注册竞赛失败");
        }
        
        return contestRegistration;
    }

    /**
     * 获取用户已注册的竞赛
     *
     * @param userId
     * @param request
     * @return
     */
    @Override
    public List<ContestVO> getRegisteredContests(Long userId, HttpServletRequest request) {
        // 1. 获取用户所有注册记录
        QueryWrapper<ContestRegistration> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        List<ContestRegistration> registrations = contestRegistrationService.list(queryWrapper);
        if (CollectionUtils.isEmpty(registrations)) {
            return new ArrayList<>();
        }
        
        // 2. 获取相应的竞赛信息
        Set<Long> contestIdSet = registrations.stream()
                .map(ContestRegistration::getContestId)
                .collect(Collectors.toSet());
        List<Contest> contestList = this.listByIds(contestIdSet);
        
        // 3. 转换为 VO
        return contestList.stream()
                .map(contest -> getContestVO(contest, request))
                .collect(Collectors.toList());
    }

    /**
     * 更新竞赛状态
     *
     * @param contest
     * @return
     */
    @Override
    public boolean updateContestStatus(Contest contest) {
        if (contest == null) {
            return false;
        }
        
        Long contestId = contest.getId();
        if (contestId == null) {
            return false;
        }
        
        // 获取竞赛当前信息
        Contest currentContest = this.getById(contestId);
        if (currentContest == null) {
            return false;
        }
        
        // 计算竞赛状态
        Date now = new Date();
        Date startTime = currentContest.getStartTime();
        Integer duration = currentContest.getDuration();
        
        Integer newStatus;
        if (startTime.after(now)) {
            // 开始时间在未来，状态为未开始
            newStatus = 0;
        } else {
            // 计算结束时间
            long endTimeMillis = startTime.getTime() + duration * 60 * 1000L;
            if (now.getTime() > endTimeMillis) {
                // 当前时间超过结束时间，状态为已结束
                newStatus = 2;
            } else {
                // 当前时间在开始和结束之间，状态为进行中
                newStatus = 1;
            }
        }
        
        // 如果状态有变化，更新状态
        if (!ObjectUtils.equals(newStatus, currentContest.getStatus())) {
            Contest updateContest = new Contest();
            updateContest.setId(contestId);
            updateContest.setStatus(newStatus);
            return this.updateById(updateContest);
        }
        
        return true;
    }
}




